using NLog;
using System.IO.Ports;

namespace TPXSDK.NET.Link;

/// <summary>
/// 串口通讯适配器, 配置字符串格式为：
/// 串口号：波特率：校验位：数据位：停止位：流控制
/// 支持多线程安全
/// </summary>
public class UartAdapter : CommAdapter
{
    #region 成员
    private readonly Logger log = LogManager.GetLogger("UartAdapter");
    private readonly SerialPort _serialPort = new();
    private readonly object _locker = new();
    private readonly List<object> _refObjs = [];
    #endregion

    /// <summary>
    /// Initializes a new instance of the <see cref="UartAdapter"/> class.
    /// </summary>
    public UartAdapter(string strDesc)
    {
        // 默认参数
        _serialPort.ReadTimeout = 200;
        _serialPort.WriteTimeout = 200;

        // 进行参数分解，并且配置
        var strPara = strDesc.Split(':');
        if (strPara.Length != 7 && strPara.Length != 8)
        {
            throw new Exception($"Invalid UartAdapter {strDesc}");
        }

        // Allow the user to set the appropriate properties.
        var result = SetPortName(strPara[0]);
        if (result) result = SetPortBaudRate(strPara[1]);
        if (result) result = SetPortParity(strPara[2]);
        if (result) result = SetPortDataBits(strPara[3]);
        if (result) result = SetPortStopBits(strPara[4]);
        if (result) result = SetFlowCtrl(strPara[5]);

        _serialPort.DtrEnable = false;
        _serialPort.RtsEnable = false;
        if (result && strPara.Length > 6)
        {
            if (strPara[6].Equals("DTR")) _serialPort.DtrEnable = true;
            if (strPara[6].Equals("RTS")) _serialPort.RtsEnable = true;
        }

        if (result && strPara.Length > 7)
        {
            if (strPara[7].Equals("DTR")) _serialPort.DtrEnable = true;
            if (strPara[7].Equals("RTS")) _serialPort.RtsEnable = true;
        }
    }

    private void OnErrorReceived(object sender, SerialErrorReceivedEventArgs e)
    {
        // System.IO.Ports.SerialErrorReceivedEventArgsRXOver
        // e.EventType ==
        log.Error(e + e.EventType.ToString());
    }

    private void OnPinChanged(object sender, SerialPinChangedEventArgs e)
    {
        // System.IO.Ports.SerialPinChangedEventArgsCtsChanged
        // e.EventType ==
        log.Error(e + e.EventType.ToString());
    }

    /// <summary>
    /// Serials the port data received.
    /// </summary>
    private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        try
        {
            while (_serialPort.BytesToRead > 0)
            {
                var data = new byte[_serialPort.BytesToRead];
                _serialPort.Read(data, 0, data.Length);
                NotifyDataRcv(data, data.Length);
            }
        }
        catch (Exception exp)
        {
            log.Error(exp.ToString());
        }
    }

    private static StopBits Str2StopBit(string stopBits)
    {
        return stopBits switch
        {
            "1" => StopBits.One,
            "1.5" => StopBits.OnePointFive,
            "2" => StopBits.Two,
            _ => throw new Exception("Invalid StopBits.")
        };
    }

    /// <summary>
    /// 打开通讯适配器
    /// </summary>
    public override bool Open()
    {
        if (_serialPort.IsOpen)
        {
            return true;
        }
        try
        {
            // 尝试打开通信接口
            _serialPort.DataReceived += OnDataReceived;
            _serialPort.PinChanged += OnPinChanged;
            _serialPort.ErrorReceived += OnErrorReceived;
            _serialPort.Open();
            return true;
        }
        catch (Exception exp)
        {
            NotifyCommError(exp.ToString());
            return false;
        }
    }

    /// <summary>
    /// 关闭适配器
    /// </summary>
    public override void Close()
    {
        // 清理事件委托函数
        _serialPort.DataReceived -= OnDataReceived;
        _serialPort.PinChanged -= OnPinChanged;
        _serialPort.ErrorReceived -= OnErrorReceived;

        // 释放串口设备
        try
        {
            _serialPort.Close();
        }
        catch(Exception exp)
        {
            // System.IO.IOException: 连到系统上的设备没有发挥作用
            log.Error(exp.ToString());
        }
    }    

    /// <summary>
    /// 添加引用对象，返回当前引用对象个数
    /// </summary>
    public override int AddRefObj(object user)
    {
        if (!_refObjs.Contains(user))
        {
            _refObjs.Add(user);
        }
        return _refObjs.Count;
    }

    /// <summary>
    /// 删除引用对象，返回当前引用对象个数，当值为零时可以安全调用 Close()
    /// </summary>
    public override int DelRefObj(object user)
    {
        _refObjs.Remove(user);
        return _refObjs.Count;
    }

    /// <inheritdoc />
    /// <summary>
    /// 通过适配器发送数据
    /// </summary>
    /// <param name="text">待发送的字符串数据</param>
    public override void Write(string text)
    {
        if (!IsOpened) return;
        try
        {
            lock (_locker)
            {
                _serialPort.Write(text);
            }
        }
        catch (Exception e)
        {
            NotifyCommError(e.ToString());
        }
    }

    /// <summary>
    /// 通过适配器发送数据
    /// </summary>
    /// <param name="buffer">待发送的无符号类型数据缓冲区.</param>
    /// <param name="offset">待发送的数据偏移量</param>
    /// <param name="count">待发送的数据长度，如果长度大于可发送的数据量则发送剩余的数据.</param>
    public override void Write(byte[] buffer, int offset, int count)
    {
        if (!IsOpened) return;
        try
        {
            lock (_locker)
            {
                _serialPort.Write(buffer, offset, count);
            }
        }
        catch (Exception e)
        {
            NotifyCommError(e.ToString());
        }
    }

    public override void Write(byte[] buffer)
    {
        if (!IsOpened) return;
        try
        {
            lock (_locker)
            {
                _serialPort.Write(buffer, 0, buffer.Length);
            }
        }
        catch (Exception e)
        {
            NotifyCommError(e.ToString());
        }
    }

    /// <summary>
    /// 设置串口号
    /// </summary>
    private bool SetPortName(string portName)
    {
        foreach (var s in PortNames)
        {
            if (!portName.Equals(s, StringComparison.OrdinalIgnoreCase))
                continue;
            _serialPort.PortName = s;
            return true;
        }
        return false;
    }

    /// <summary>
    /// 设置波特率
    /// </summary>
    private bool SetPortBaudRate(string portBandRate)
    {
        try
        {
            _serialPort.BaudRate = int.Parse(portBandRate);
        }
        catch (Exception)
        {
            throw new Exception("Invalid COM BandRate.");
        }
        return true;
    }

    /// <summary>
    /// 设置校验位
    /// </summary>
    private bool SetPortParity(string portParity)
    {
        foreach (var s in Enum.GetNames(typeof(Parity)))
        {
            if (!portParity.Equals(s, StringComparison.OrdinalIgnoreCase))
                continue;
            _serialPort.Parity = (Parity)Enum.Parse(typeof(Parity), s);
            return true;
        }
        throw new Exception("Invalid COM Parity.");
    }

    /// <summary>
    /// 设置数据位
    /// </summary>
    private bool SetPortDataBits(string portDataBits)
    {
        try
        {
            _serialPort.DataBits = int.Parse(portDataBits);
        }
        catch (Exception)
        {
            throw new Exception("Invalid COM DataBits.");
        }
        return true;
    }

    /// <summary>
    /// 设置停止位
    /// </summary>
    private bool SetPortStopBits(string portStopBits)
    {
        _serialPort.StopBits = Str2StopBit(portStopBits);
        return true;
    }

    /// <summary>
    /// 设置流控位，即所谓的“握手协议”
    /// </summary>
    private bool SetFlowCtrl(string flowCtrl)
    {
        if (flowCtrl.Equals("NONE"))
        {// 没有用于握手的控件
            _serialPort.Handshake = Handshake.None;
        }
        else if (flowCtrl.Equals("SOFT"))
        {
            // 使用 XON/XOFF 软件控制协议。
            // 发送 XOFF 控制以停止数据传输。
            // 发送 XON 控制以继续传输。
            // 使用这些软件控制，而不是使用请求发送 (RTS) 和清除发送 (CTS) 硬件控制。
            _serialPort.Handshake = Handshake.XOnXOff;
        }
        else if (flowCtrl.Equals("HARD"))
        {
            // 使用请求发送 (RTS) 硬件流控制。
            // RTS 发出信号，指出数据可用于传输。
            // 如果输入缓冲区已满，RTS 行将被设置为 false。
            // 当输入缓冲区中有更多可用空间时，RTS 行将被设置为 true。
            _serialPort.Handshake = Handshake.RequestToSend;
        }
        return true;
    }

    /// <summary>
    /// 获得串口名称集合
    /// </summary>
    public static string[] PortNames
    {
        get
        {
            var portNames = SerialPort.GetPortNames().Distinct().ToArray();
            var formatNames = new string[portNames.Length];

            for (var i = 0; i < portNames.Length; i++)
            {
                formatNames[i] = FormatPortName(portNames[i]);
            }

            Array.Sort(formatNames);
            return formatNames;
        }
    }

    private static string FormatPortName(string portName)
    {
        var i = 0;
        var isNum = false;
        var isChange = false;
        var index = portName.Length;
        var portNameArray = portName.ToCharArray();

        foreach (var curChar in portNameArray)
        {
            i++;
            if (char.IsNumber(curChar))
            {
                isNum = true;
                isChange = true;
            }
            else
            {
                if (!isChange)
                {
                }
                isChange = false;
            }

            if (!isNum || isChange) continue;
            index = i - 1;
            break;
        }

        return portName[..index];
    }

    public override bool IsOpened
    {
        get { return _serialPort.IsOpen; }
    }
}//end UartAdapter

